﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

using alib.Debugging;
using alib.Enumerable;
using alib.String;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public partial struct CoverageMap : IProximalContext<CoverageMap>
	{
		public CoverageMap(GenerationInput input)
		{
			Debug.Assert(input != null);

			this.input = input;
			this.c_covered = 0;
			this.covered_eps = 0;
			this.access_vars = 0;
		}
		public CoverageMap(GenerationInput input, ulong covered_eps, int c_covered, ulong access_vars)
		{
			Debug.Assert(c_covered == alib.Bits._bitarray_ext.OnesCount(covered_eps));

			this.input = input;
			this.c_covered = c_covered;
			this.covered_eps = covered_eps;
			this.access_vars = access_vars;
		}

		public GenerationInput input;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int c_covered;

		[DebuggerDisplay("{ctrl.input_mrs.DisplayRelations(covered_eps),nq}")]
		public ulong covered_eps;

		[DebuggerDisplay("{ctrl.input_mrs.DisplayVariables(access_vars),nq}")]
		public ulong access_vars;

		public int CoverageCount { get { return c_covered; } }

		/// <summary>
		/// John Carroll and Stephan Oepen (2005) `High efficiency realization for a wide-coverage unification 
		/// grammar.'  In R. Dale and K-F. Wong (eds.) Proceedings of the Second International Joint Conference 
		/// on Natural Language Processing (Best Paper, IJCNLP05), Jeju Island, Korea. Springer Lecture Notes 
		/// in Artificial Intelligence, Volume 3651. 165-176.
		/// </summary>
		public bool RecomputeAccessible(ArrayTfs new_mother)
		{
			Debug.Assert(new_mother.ArgsRestricted);

			/// If the TFS is restricted, the accessible vars are detected when the TFS is realized.
			/// Otherwise, we have to do a graph traversal to find them.
			ulong computed = new_mother.SemanticsRestricted ? new_mother.varbits : new_mother.FindAccessibleIndices();
			ulong changed = this.access_vars ^ computed;

			if (changed != 0)
			{
				/// this must still be set even when returning false, since the chart edge is checked for
				/// other uses
				this.access_vars = computed;

				/// extract the indexes for the uncovered EPs using an O(n) method
				ulong not_covered = ~this.covered_eps & input.AllRelsMask;
				while (not_covered != 0)
				{
					computed = not_covered & (ulong)-(long)not_covered;
					int ix =
						(0 == (computed & 0xAAAAAAAAAAAAAAAAUL) ? 0 : 1) +
						(0 == (computed & 0xCCCCCCCCCCCCCCCCUL) ? 0 : 2) +
						(0 == (computed & 0xF0F0F0F0F0F0F0F0UL) ? 0 : 4) +
						(0 == (computed & 0xFF00FF00FF00FF00UL) ? 0 : 8) +
						(0 == (computed & 0xFFFF0000FFFF0000UL) ? 0 : 16) +
							 ((computed < 0xFFFFFFFF00000000UL) ? 0 : 32);

					if ((input.RelVarbits[ix] & changed) > 0UL)
						return false;
					not_covered ^= computed;
				}
			}
			return true;
		}

		public bool CanCombineWith(CoverageMap other)
		{
			return (this.covered_eps & other.covered_eps) == 0;
		}

		public bool CombineWith(CoverageMap other, out CoverageMap cm_out)
		{
			if (this.c_covered == 0)
				cm_out = other;
			else if (other.c_covered == 0)
				cm_out = this;
			else if ((this.covered_eps & other.covered_eps) == 0)
			{
				cm_out = new CoverageMap();
				cm_out.input = this.input;
				cm_out.c_covered = this.c_covered + other.c_covered;
				cm_out.covered_eps = this.covered_eps | other.covered_eps;
				cm_out.access_vars = this.access_vars | other.access_vars;
			}
			else
			{
				cm_out = default(CoverageMap);
				return false;
			}
			return true;
		}

		public override String ToString()
		{
#if !RELS_MRS
#if true
			if (input == null)
				return "default(CoverageMap)";

			var inp = input;

			//var rels = input_mrs.rels;
			//return String.Format("EPs[{0}]  +[{1}]",
			//	SetBits.Select(i => String.Format("{0}:{1}", i, inp.Rels[i].Pred.Left(6))).StringJoin(", "),
			//	input_mrs.DisplayVariables(access_vars));

			return String.Format("EPs[{0}]",
				SetBits.Select(i => String.Format("{0}:{1}", i, inp.mrsm.tu.TypeNameOrStringValue(inp.RelKeys[i].pred).SubstringOrLess(0, 6))).StringJoin(", "));
#else
			return alib.Bits._bitarray_ext.DisplayUlongBits(bits,input_mrs.RelsCount);
#endif
#else
			return "(ToString)";
#endif
		}


#if false
		public ulong CoveredVarbits
		{
			get
			{
				ulong all_varbits = 0;
				ulong msk = 1;
				int c = input_mrs.RelsCount;
				for (int i = 0; i < c; i++)
				{
					if ((covered_eps & msk) != 0)
						all_varbits |= input_mrs.rels[i].varbits;
					msk <<= 1;
				}
				return all_varbits;
			}
		}
#endif

		public String BaString()
		{
			int c = input.RelSlots.Length;
			Char[] rgch = new Char[c];
			ulong msk = 1;
			for (int i = 0; i < c; i++)
			{
				rgch[i] = (covered_eps & msk) == 0 ? '_' : (Char)('a' + i);
				msk <<= 1;
			}
			return new String(rgch);
		}
#if false
		public static CoverageMap operator |(CoverageMap cm1, CoverageMap cm2)
		{
			return new CoverageMap(
						cm1.input_mrs,
						cm1.covered_eps | cm2.covered_eps,
						cm1.access_vars | cm2.access_vars);
		}
		public bool Equals(CoverageMap other)
		{
			return covered_eps == other.covered_eps && access_vars == other.access_vars;
		}
		public static bool operator ==(CoverageMap x, CoverageMap y)
		{
			return x.covered_eps == y.covered_eps && x.access_vars == y.access_vars;
		}
		public static bool operator !=(CoverageMap x, CoverageMap y)
		{
			return x.covered_eps != y.covered_eps || x.access_vars != y.access_vars;
		}
		public override bool Equals(object obj) { throw not.impl; }
		public override int GetHashCode() { throw not.impl; }
#endif
	};


}
